/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package drivemachine.data2;

import drivemachine.Scanner;
import drivemachine.Util;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.List;

/**
 *
 * @author positron
 */
public class FolderEntry extends Entry<String> {
	//public final List<FolderEntry> children = new ArrayList<>(50);
	protected String name;
	public enum State { NOT_TRAVERSED(0), TRAVERSED(1), LINK(2), DIFFERENT_MOUNT(3);
		private final byte id;
		State(int i) {id=(byte)i;}
		private static final State[] values = values();
		public static State byId(byte v) { return values[v]; }
	}

	
	private State state = State.NOT_TRAVERSED;

	//transient fields are only needed for scanning
	private transient File path = null;
	private transient String mount = null;


	public FolderEntry(FolderEntry parent, String name) {
		super(parent, name);
		path = new File(parent.getFile(), name);
		selfsize = 0;
		subsize = 0;
	}
	public FolderEntry(String name) {
		super(name);
		path = new File(name);
		selfsize = 0;
		subsize = 0;
	}

	public String getPath() {
		return path.toString();
	}
	protected File getFile() { return path; }

	public boolean hasSize() {return subsize>=0 || selfsize>=0; }

	protected long date;

	public long getDate() {	return date; }

	@Override
	public String toString() {
		switch (state) {
			case LINK : return filename+" (link)";
			case DIFFERENT_MOUNT: return filename+" (diff mount)";
			case TRAVERSED:
				if (!hasSize())return filename;
				return filename+" ("+sizeToString(getFullSize())+")";
			case NOT_TRAVERSED: return filename +" (n/a)";
		}
		return filename +" (?)";
	}

	public void traverse(Scanner sc) {
		selfsize = 0;
		subsize=0;
		Path filepath = path.toPath();
		sc.incFolderCount();

		BasicFileAttributes attrs = null;
		try {
			attrs = Files.readAttributes(filepath,
					BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
		} catch(IOException e) {
			e.printStackTrace();
			return;
		}

		try {
			date = Files.getLastModifiedTime(filepath).toMillis();
		} catch(IOException e) {
			e.printStackTrace();
			return;
		}

		if( Util.isLink(filepath, attrs) ) {
			state = State.LINK;
			return;
		}

		String spath = path.toString();
		List<String> mounts = Util.listMountpoints();
		for(String m: mounts)
			if(spath.startsWith(m)) mount = m;
		if(parent!=null && !mount.equals( ((FolderEntry)parent).mount)) {
			state = State.DIFFERENT_MOUNT;
			return;
		}
		
		//System.out.println("Traversing "+this);
		try (DirectoryStream<Path> stream = Files.newDirectoryStream(filepath)) {
			for (Path f: stream) {
				File ff = f.toFile();
				if( ff.isFile() ) selfsize += Files.size(f); else
				if( ff.isDirectory() ) children.add(
						new FolderEntry(this, f.getFileName().toString() )
				);

			}
		} catch(AccessDeniedException e) {
			System.out.println("Access denied for "+filepath);
		} catch (IOException | DirectoryIteratorException x)  {
			System.err.println(x);
		}

		for(Entry e: children) {
			((FolderEntry)e).traverse(sc);
			subsize += e.getFullSize();
		}
		Collections.sort(children);
		Collections.reverse(children);
		state = State.TRAVERSED;
	}

	public static FolderEntry read(DataInputStream is, FolderEntry e) throws IOException {
		FolderEntry res;
		String fs = is.readUTF();
		if(e==null)
			res = new FolderEntry(fs);
		else
			res = new FolderEntry(e, fs);
		res.date = is.readLong();
		res.selfsize = is.readLong();
		res.subsize = is.readLong();
		res.state = State.TRAVERSED;

		int n = is.readInt();
		for(int i=0; i<n; i++)
			res.children.add( FolderEntry.read(is, res));
		return res;
	}

	public void save(DataOutputStream os)throws IOException {
		os.writeUTF(filename);
		os.writeLong(date);
		os.writeLong(selfsize);
		os.writeLong(subsize);
		os.writeInt(children.size());
		for(Entry e: children)
			((FolderEntry)e).save(os);
	}
	
	public static FolderEntry read3(DataInputStream is, FolderEntry e) throws IOException {
		FolderEntry res;
		String fs = is.readUTF();
		if(e==null)
			res = new FolderEntry(fs);
		else
			res = new FolderEntry(e, fs);
		res.date = is.readLong();
		res.selfsize = is.readLong();
		res.subsize = is.readLong();
		res.state = State.byId(is.readByte() );

		int n = is.readInt();
		for(int i=0; i<n; i++)
			res.children.add( FolderEntry.read3(is, res));
		return res;
	}

	public void save3(DataOutputStream os)throws IOException {
		os.writeUTF(filename);
		os.writeLong(date);
		os.writeLong(selfsize);
		os.writeLong(subsize);
		os.writeByte(state.id);
		os.writeInt(children.size());
		for(Entry e: children)
			((FolderEntry)e).save3(os);
	}
}
